package p.ithorns.sample.refresh;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.bind.BindResult;
import org.springframework.boot.context.properties.bind.Binder;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * ValueHolder
 *
 * @author HtL
 * @date 2025/3/14 15:36
 * @since 1.0.0
 */
@Getter
@Setter
public class ValueHolder {
    /**
     * Spring Bean Name
     */
    private String beanName;
    /**
     * bean示例的弱引用
     */
    private WeakReference<Object> beanRef;
    /**
     * 配置对应的key： 如 config.user
     */
    private String key;
    /**
     * 配置引用，如 ${config.user}
     */
    private String placeholder;
    /**
     * 配置绑定的目标类型
     */
    private Class<?> targetType;

    Consumer<Binder> updater;

    public ValueHolder(String key, String placeholder, Object bean, String beanName, Field field) {
        this.key = key;
        this.placeholder = placeholder;
        this.beanRef = new WeakReference<>(bean);
        this.beanName = beanName;
        this.targetType = field.getType();

        this.updater = update(field);
    }

    public ValueHolder(String key, String placeholder, Object bean, String beanName, Method method) {
        this.key = key;
        this.placeholder = placeholder;
        this.beanRef = new WeakReference<>(bean);
        this.beanName = beanName;
        this.updater = update(method);
        this.targetType = method.getParameterTypes()[0];
    }

    public Object getBean() {
        return beanRef.get();
    }

    private Consumer<Binder> update(final Field f) {
        return b -> {
            Object value = b.bindOrCreate(this.key, targetType);
            f.setAccessible(true);
            try {
                f.set(getBean(), value);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        };
    }

    private Consumer<Binder> update(final Method m) {
        return b -> {
            Object value = b.bindOrCreate(this.key, targetType);
            m.setAccessible(true);
            try {
                m.invoke(getBean(), value);
            } catch (IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        };
    }

}